home *** CD-ROM | disk | FTP | other *** search
- /*
- File Driver.c, the SCSI Hard Disk Driver
- Leo Drizis, March 1989
- Only for personal use, commercial use prohibited !!!
-
- Compile this file with THINK'S Lightspeed C
- and link it with the <MacTraps> library
- */
-
- #include <DiskDvr.h>
- #include <DeviceMgr.h>
- #include <MemoryMgr.h>
- #include <SCSIMgr.h>
- #include <FileMgr.h>
-
- #define DRVRSIZE 4000 /* an estimate for the driver length */
- #define BLKSIZE 512 /* the sector size of our disk */
- #define SELECTERR -2
- #define SENDCMDERR -3
- #define COMPLERR -4
- #define SENSEERR -7 /* define some error codes */
- #define MAXDRIVES 7 /* max number of partitions */
-
- #include "SCSITypes.h" /* SCSI commands & data structures */
-
- extern DrvQElPtr DrvQHdrHead : 0x30A; /* start of the installed drives' queue */
- extern Ptr ScrnBase : 0x824; /* start of the screen bitmap in memory */
-
- #include "Driver.prot" /* delete this line if you don't use prototypes */
-
- scsiCmd parkCmd = {0x1B,0,0,1,0}; /* park heads command */
- scsiCmd currCmd; /* current read/write command */
- scsiCmd reqSenCmd = {3,0,0,0,0}; /* request sense command */
- unsigned long defSct; /* the last defect sector detected */
- char isBlind; /* if we use blind reads/writes */
- tib tib1,tib2,tib3; /* tibs for read/write */
- Boolean isShDwInst; /* if park heads on shutdown installed */
- unsigned int currID, /* the SCSI-ID for this driver */
- firstDrive, /* the drive number of the 1st partition */
- nDrives; /* the number of found partitions */
- myStruct drStats[MAXDRIVES]; /* status parameters for each partition */
-
-
- Jumper() /* jumps to our main(), so that we can include the A4 routines */
- {
- asm { JMP main }
- }
-
-
- #include <SetUpA4.h> /* lightspeed C file to adjust for access of globals */
-
-
- main()
- {
- THz currZon; /* the current memory zone */
- register unsigned long ID; /* the ID number for this SCSI device */
- register unsigned int DrRefNum; /* reference no of our driver */
- long err;
- register DCtlPtr DrEntry; /* entry of driver into device map */
- register Ptr DrStart; /* start location of driver */
-
-
- goto start; /* skip the driver header */
- asm {
- @DrvrHead: /* this is the driver header. normal (non-SCSI) drivers */
- /* should start here */
- DC.W 0x6F00 ; Flags 0110 1111 0000 0000
- DC.W 0x0000 ; Delay
- DC.W 0x0000 ; Mask
- DC.W 0x0000 ; Menu
- DC.W @Open+8 /* points to our open routine */
- DC.W @Prime+10 /* points to our prime routine */
- DC.W @Control+12 /* points to our control routine */
- DC.W @Status+14 /* points to our status routine */
- DC.W @Close+16 /* points to our close routine */
- DC.B 9,'.','B','l','a','s','t','e','r',' ' ; Driver name
- /* the "glue" routines follow. they push A0 and A1 into the stack
- so that their C "brothers" can access them correctly */
- @Open /* does really nothing ! */
- MOVEQ #0,D0
- MOVE D0,16(A0) ; ioResult
- BRA.S @end
- @Close /*
- MOVE.L A0,-(A7) /* push A0 & A1 */
- MOVE.L A1,-(A7)
- JSR Close1 /* call C routine */
- BRA.S @clrStack
- @Control
- MOVE.L A0,-(A7) /* push A0 & A1 */
- MOVE.L A1,-(A7)
- JSR Control1 /* call C routine */
- BRA.S @clrStack
- @Status
- MOVE.L A0,-(A7) /* push A0 & A1 */
- MOVE.L A1,-(A7)
- JSR Status1 /* call C routine */
- BRA.S @clrStack
- @Prime
- MOVE.L A0,-(A7) /* push A0 & A1 */
- MOVE.L A1,-(A7)
- JSR Prime1 /* call C routine */
- @clrStack
- MOVE.L (A7)+,A1 /* remove A0 & A1 from stack */
- MOVE.L (A7)+,A0
- @end
- MOVE D0,0x142 ; store error code in DskErr
- BTST #9,6(A0) ; examine if immediate call in ioTrap
- BNE.S @exit
- MOVE.L 0x8FC,-(A7) ; jIoDone
- @exit
- RTS
- }
-
- start:
- asm { move.l D5,ID } /* on entry to the driver, D5 contains the
- SCSI-ID number of the drive in use. NOWHERE DOCUMENTED! */
- SetPtrSize(Jumper,DRVRSIZE); /* lock driver in memory */
- if (ID >= 0) {
- currZon = TheZone; /* save current zone */
- SetApplBase(SysZone->bkLim + DRVRSIZE); /* expand the system zone */
- TheZone = currZon; /* restore zone */
- }
- DrRefNum = ~( (ID & 0xFFFF) +32); /* calculate reference number of driver
- from SCSI-ID number */
- asm {
- MOVEQ #0,D0
- MOVE.W DrRefNum,D0
- DC.W 0xA03D /* this is the _DrvrInstall trap. it installs a device
- driver in the system. the driver ref-num is in D0. NOWHERE DOCUMENTED! */
- MOVEQ #0,D0
- MOVE.W ID,D0
- ADD.L #32,D0
- ASL #2,D0 ; calculate offset to our driver's entry
- MOVEA.L 0x11C,DrEntry ; uTableBase
- MOVEA.L 0(DrEntry,D0.W),A0 ; get our driver's entry
- MOVEA.L (A0),DrEntry
- _HLock ; (A0/h:Handle) ; lock driver in memory
- LEA @DrvrHead,DrStart ; get our driver's header
- MOVE.L DrStart,(DrEntry) ; install driver's parameters
- MOVE (DrStart)+,4(DrEntry) ; dCtlFlags
- MOVE.L (DrStart)+,34(DrEntry) ; dCtlDelay & dCtlEmask
- MOVE (DrStart)+,38(DrEntry) ; dCtlMenu
- BSET #5,5(DrEntry) ; declare that driver is open (dCtlFlags+1)
- BCLR #6,5(DrEntry) ; declare that driver is ROM based (dCtlFlags+1)
- LEA Jumper,A0 ; get starting address of our code
- } /* end of the horrible assembler part ! */
- RememberA0(); /* save starting address of our code (needed to access globals) */
- err = myInstall(DrEntry); /* install filesystem */
- if (err != 0) return(-23); /* Open error */
- else return(0);
- }
-
-
- myInstall(DCE) /* install filesystem */
- DCtlPtr DCE; /* Data Control Entry to our driver */
- {
- register long i;
- register Boolean FSFound; /* if we found a filesystem partition */
- DrvQElPtr D0;
- register int drvNum; /* current drive number */
- Partition prtt; /* partition description */
- Boolean drvrFound; /* if we found a driver partition */
- int pMapLen; /* length of partition map */
- register int err;
- register myStruct *currDr; /* pointer to current partitions' data */
-
- SetUpA4(); /* adjust A4 to access global variables correctly */
- isShDwInst = FALSE;
- currID = ~DCE->dCtlRefNum - 32; /* find which SCSI-ID we have */
- /* set up our tibs */
- tib1.cmd = scInc; /* read/write and increase pointer */
- tib1.cnt = BLKSIZE; /* how many bytes to read/write */
- tib2.cmd = scLoop; /* loop to previous tib, the no of loops is set later */
- tib2.add = (Ptr)-10;
- tib3.cmd = scStop; /* stop reading/writing */
- FSFound = FALSE; /* set up some variables */
- drvrFound = FALSE;
- pMapLen = 1;
- nDrives = 0;
- isBlind = TRUE;
- for (i=1; i<=pMapLen && nDrives<MAXDRIVES; i++) {
- /* for all entries in the partition map */
- err = rwBlock(&prtt,i,1,FALSE,currID); /* read the p-map entry */
- if (err != 0 && err != COMPLERR) continue; /* skip if error */
- if (prtt.pmSig != 0x504D) continue; /* skip if no valid partition */
- if ((prtt.pmPartStatus & 0x0010) == 0) continue; /* partition hidden */
- if (i == 1) pMapLen = prtt.pmMapBlkCnt; /* get the real p-map length */
- if (drvrFound == FALSE &&
- strcmp(prtt.pmParType,"Apple_Driver") == 0 ) {
- drvrFound = TRUE; /* we found a driver partition */
- continue;
- }
- if (strcmp(prtt.pmParType,"Apple_HFS") == 0) {
- /* we found a filesystem partition */
- drvNum=8; /* SCSI-drives start from no 8 */
- NextD: D0 = DrvQHdrHead; /* search the drive queue to find a free drive no */
- while (D0 != 0) {
- if (D0->dQDrive == drvNum) {
- drvNum++;
- goto NextD;
- }
- else D0 = D0->qLink;
- }
- currDr = &drStats[nDrives]; /* get start address of this partition's
- status variables. avoids the very slow continuous array operations */
- currDr->myStats.track = 0;
- if (prtt.pmPartStatus & 0x0020) currDr->myStats.writeProt = 0;
- else currDr->myStats.writeProt = 0x80; /* if write-protected */
- currDr->myStats.diskInPlace = 8; /* show that this is a hard disk */
- currDr->myStats.installed = 1;
- currDr->myStats.sides = 0;
- currDr->myStats.qType = 1;
- currDr->myStats.qLink = 0;
- currDr->myStats.dQDrive = drvNum; /* drive number */
- currDr->myStats.dQRefNum = DCE->dCtlRefNum; /* driver ref-num */
- currDr->myStats.dQFSID = 0; /* apple's filesystem */
- currDr->myStats.driveSize = prtt.pmDataCnt & 0xFFFF;
- currDr->myStats.driveS1 = prtt.pmDataCnt >> 16; /* no of sectors */
- currDr->myStats.driveManf = 0;
- currDr->myStats.driveChar = 0;
- currDr->myStats.driveMisc = 0;
- currDr->dataStart = prtt.pmPyPartStart; /* first sector of partition */
- currDr->dataCnt = prtt.pmDataCnt; /* no of sectors of partition */
- currDr->myStats.driveType = drvNum; /* drive number */
- AddDrive(DCE->dCtlRefNum,drvNum,&currDr->myStats.qLink);
- /* declare that a new drive is connected. NOWHERE DOCUMENTED! */
- if (FSFound == FALSE) firstDrive = drvNum; /* remember the 1st drive */
- nDrives++; /* one more filesystem partition found */
- FSFound = TRUE;
- }
- }
- if (drvrFound == FALSE) { /* handle some errors */
- RestoreA4();
- return(-2);
- }
- if (FSFound == FALSE) { /* no filesystem isn't really an error! */
- RestoreA4();
- return(0);
- }
- RestoreA4(); /* restore A4 to its old value */
- return(0);
- }
-
-
- Close1(DCE,PBP) /* close our driver */
- ParmBlkPtr PBP; /* a read/write parameter block */
- DCtlPtr DCE; /* Data Control Entry to our driver */
- {
- int err,dr;
- register myStruct *currDr; /* pointer to current partitions' data */
-
- SetUpA4(); /* adjust A4 to access global variables correctly */
- for (dr=0; dr<nDrives; dr++) {
- err = Dequeue( drStats[dr].myStats.qLink, &DrvQHdr);
- }
- if (isShDwInst == TRUE) ShutDwnRemove(ParkDrive);
- RestoreA4(); /* restore A4 to its old value */
- }
-
-
- Prime1(DCE,PBP) /* read/write operations */
- ParmBlkPtr PBP; /* a read/write parameter block */
- DCtlPtr DCE; /* Data Control Entry to our driver */
- {
- Boolean isWrite; /* if it is a write call */
- register unsigned long posit,nSect;
- register int err;
- register long lastDefect;
- register int retries;
- register myStruct *currDr; /* pointer to current partitions' data */
-
- SetUpA4(); /* adjust A4 to access global variables correctly */
- currDr = &drStats[PBP->ioParam.ioVRefNum - firstDrive]; /* which partition ? */
- isWrite = ((PBP->ioParam.ioTrap & 0x00FF) == 3); /* is it read or write ? */
- posit = DCE->dCtlPosition >> 9; /* starting sector for read/write */
- nSect = PBP->ioParam.ioReqCount >> 9; /* no of sectors */
- if (PBP->ioParam.ioVRefNum != currDr->myStats.driveType) {
- RestoreA4();
- return(-56); /* No such drive ! */
- }
- if ((PBP->ioParam.ioReqCount & 0x1FF) != 0 &&
- currDr->dataStart != 0 &&
- currDr->dataCnt < nSect + posit) {
- RestoreA4();
- return(-50); /* Param list error, no such sector ! */
- }
- if ((PBP->ioParam.ioPosMode & 0x40) != 0 && isWrite == FALSE) err = 0;
- /* if we don't need to verify writes */
- else {
- for (retries=0, lastDefect=-1; retries < 4; ) {
- err = rwBlock(PBP->ioParam.ioBuffer,currDr->dataStart+posit,
- nSect,isWrite,currID); /* read/write the sectors */
- if (err == 0) goto OK; /* successful */
- if (lastDefect == defSct) retries++; /* the same defect */
- else {
- retries = 0;
- lastDefect = defSct; /* another defect, give it 4 more tries */
- }
- } /* try 4 times to read/write */
- PBP->ioParam.ioActCount = 0; /* no sectors read/written */
- RestoreA4();
- return(-36); /* I/O error */
- }
- OK: /* operation successful */
- PBP->ioParam.ioActCount = PBP->ioParam.ioReqCount; /* how many sectors r/w */
- DCE->dCtlPosition += PBP->ioParam.ioReqCount; /* adjust position */
- RestoreA4(); /* restore A4 to its old value */
- return(err);
- }
-
-
- Control1(DCE,PBP) /* control operations */
- ParmBlkPtr PBP; /* a read/write parameter block */
- DCtlPtr DCE; /* Data Control Entry to our driver */
- {
- int res;
- register int sw,dr;
- Ptr icnPtr; /* pointer to disk icon */
- register myStruct *currDr; /* pointer to current partitions' data */
-
- SetUpA4(); /* adjust A4 to access global variables correctly */
- res = 0;
- currDr = &drStats[PBP->ioParam.ioVRefNum - firstDrive]; /* which partition ? */
- if (PBP->cntrlParam.csCode == 65) { /* make periodic action */
- if (isShDwInst == FALSE &&
- GetTrapAddress(0x95) != GetTrapAddress(0x9F)) {
- /* check if ShutDown is UnImplemented */
- ShutDwnInstall(ParkDrive,sdOnPowerOff); /* install ShutDown Proc */
- isShDwInst = TRUE;
- }
- for (dr=0; dr<nDrives; dr++) /* "insert" our partitions' disks */
- res = PostEvent(7,drStats[dr].myStats.driveType);
- DCE->dCtlFlags &= 0x4FFF; /* at intr level,no goodbye,no time */
- goto exit;
- }
- if (currDr->myStats.driveType != PBP->ioParam.ioVRefNum) {
- res = -56; /* no such drive */
- goto exit;
- }
- sw = PBP->cntrlParam.csCode;
- switch (sw) {
- case 5: /* verify disk in place (is always in place!) */
- break;
- case 6: /* format */
- break; /* the driver must NOT format the drive ! */
- case 21:
- case 22: /* return ICN# + data */
- icnPtr = GetICN(); /* get icon pointer */
- *(icnPtr + 256 + 6) = currID + '0'; /* set our SCSI number */
- BlockMove(&icnPtr,PBP->cntrlParam.csParam,4);
- break;
- case 7: /* eject */
- res = PostEvent(7,currDr->myStats.driveType);
- res = -17; /* cannot eject, unimplemented control instruction */
- break;
- default:
- res = -17; /* unimpl contr instr */
- break;
- }
- exit:
- RestoreA4(); /* restore A4 to its old value */
- return(res);
- }
-
-
- Status1(DCE,PBP) /* status of partition/drive */
- ParmBlkPtr PBP; /* a read/write parameter block */
- DCtlPtr DCE; /* Data Control Entry to our driver */
- {
- register int res;
- register myStruct *currDr; /* pointer to current partitions' data */
-
- SetUpA4(); /* adjust A4 to access global variables correctly */
- res = 0;
- currDr = &drStats[PBP->ioParam.ioVRefNum - firstDrive]; /* which partition ? */
- if (currDr->myStats.driveType != PBP->ioParam.ioVRefNum) {
- res = -56; /* no such drive */
- goto exit;
- }
- if (PBP->cntrlParam.csCode == 8) { /* drive status */
- BlockMove(&currDr->myStats,PBP->cntrlParam.csParam,22);
- goto exit; /* return our status parameters */
- }
- res = -18; /* cannot respond to this call */
- exit:
- RestoreA4(); /* restore A4 to its old value */
- return(res);
- }
-
-
- void ParkDrive() /* is automatically called on shutdown */
- {
- SetUpA4(); /* adjust A4 to access global variables correctly */
- mySCSI(900L,FALSE,FALSE,(Ptr)0L,currID,6,&parkCmd); /* park heads */
- RestoreA4(); /* restore A4 to its old value */
- }
-
-
- rwBlock(buffer,LBA,nBlk,isWrite,id) /* reads/writes sectors */
- Ptr buffer; /* where is the data ? */
- register unsigned long LBA; /* starting sector */
- register unsigned int nBlk; /* how many sectors */
- Boolean isWrite; /* write or read ? */
- unsigned int id; /* SCSI ID */
- {
- register unsigned int actNBlk; /* internal read count */
- register int err;
- senseBytes senseByt;
-
- if (isWrite) currCmd.opCode = 0xA; /* write command opcode */
- else currCmd.opCode = 0x8; /* read */
- tib1.add = buffer; /* set r/w address in tib */
- while (nBlk > 0) { /* for all sectors requested */
- currCmd.LUN = LBA >> 16;
- currCmd.LBA = LBA;
- if (nBlk > 256) actNBlk = 256; /* cannot more than 256 sectors at once */
- else actNBlk = nBlk;
- tib2.cnt = actNBlk; /* set no of sectors in tib */
- currCmd.lng = actNBlk;
- err = mySCSI(180*60L,isBlind,isWrite,&tib1,id,6,&currCmd);/* send command */
- if (err != 0) {
- if (err != 2) return(err);
- else { /* we must ask for sense info */
- err = reqSense(&senseByt,8);
- if (err != 0) return(SENSEERR);
- else {
- defSct = senseByt.LBA; /* defect sector number */
- return(senseByt.senseKey);
- }
- }
- }
- else {
- nBlk -= actNBlk; /* adjust for next read/write */
- LBA += actNBlk;
- }
- } /* while */
- return(err);
- }
-
-
- mySCSI(time,isBli,isWrite,tibPtr,id,cmdLen,cmdPtr) /* send an SCSI command */
- unsigned long time; /* how long we must wait */
- Boolean isBli,isWrite;
- Ptr tibPtr; /* pointer to our tib, NULL if no data transfer */
- unsigned int cmdLen,id; /* length of command, SCSI ID number */
- Ptr cmdPtr; /* pointer to command */
- {
- register int res,err;
- register int try; /* no of retries */
- int stat,messg;
-
- res = 0;
- *ScrnBase ^= 0xC0; /* invert upper-left bit of screen */
- GetBus: /* try INDEFINITELY to get control of the SCSI-bus */
- for (try=0; try<4; try++) {
- if (SCSIGet() == 0) goto GotIt;
- }
- for (;;) if ((SCSIStat() & 0x0040) == 0) goto GetBus; /* not busy */
- GotIt: /* got control */
- err = SCSISelect(id); /* select the drive */
- if (err != 0) return(SELECTERR);
- for (try=0; try<4; try++) { /* try to send the command */
- err = SCSICmd(cmdPtr,cmdLen);
- if (err == 0) goto CmdOK;
- }
- res = SENDCMDERR;
- goto Complete;
- CmdOK: /* command sent, now transfer data */
- if (tibPtr != 0) { /* if there are data to send */
- if (isBli) { /* blind transfer */
- if (isWrite) err = SCSIWBlind(tibPtr);
- else err = SCSIRBlind(tibPtr);
- }
- else { /* non blind */
- if (isWrite) err = SCSIWrite(tibPtr);
- else err = SCSIRead(tibPtr);
- }
- res = err;
- }
- Complete: /* wait for command to complete */
- err = SCSIComplete(&stat,&messg,time);
- *ScrnBase ^= 0xC0; /* invert upper-left bit of screen */
- if (err != 0) return(COMPLERR);
- else {
- if (res != 0 && stat == 2) return(stat);
- return(res);
- }
- }
-
-
- reqSense(buffer,count) /* does a request sense to find which error occured */
- Ptr buffer; /* where to store sense data */
- unsigned int count; /* how many data to request */
- {
- tib tib1,tib2;
- register int res;
-
- reqSenCmd.lng = count;
- tib1.cmd = scNoInc; /* read and don't increase buffer */
- tib1.add = buffer;
- tib1.cnt = count;
- tib2.cmd = scStop;
- res = mySCSI(600L,FALSE,FALSE,&tib1,currID,6,&reqSenCmd);
- return(res);
- }
-
-
- Ptr GetICN() /* returns a pointer to disk icon */
- {
- register Ptr addr;
-
- asm {
- BRA @doIt
- @icndata /* this is the icon */
- DC.W 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
- DC.W 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
- DC.W 0x0000,0x0000,0x000F,0xC000,0x0030,0x3000,0x00C0,0x0C00
- DC.W 0x0100,0x0210,0x0200,0x0128,0x0200,0x01C4,0x0400,0x009A
- DC.W 0x0407,0x8119,0x080F,0xC102,0x081F,0xE204,0x081F,0xE208
- DC.W 0x081F,0xE430,0x081F,0xE4C0,0x080F,0xCB40,0x0407,0x9480
- DC.W 0x0400,0x2880,0x0200,0x3100,0x0200,0x0100,0x0100,0x0200
- DC.W 0x00C0,0x0C00,0x0030,0x3000,0x000F,0xC000,0x0000,0x0000
- ; and this is the mask
- DC.W 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
- DC.W 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
- DC.W 0x0000,0x0000,0x0000,0x0000,0x000F,0xC000,0x003F,0xF000
- DC.W 0x00FF,0xFC00,0x01FF,0xFE10,0x01FF,0xFE38,0x03FF,0xFF64
- DC.W 0x03F8,0x7EE6,0x07F0,0x3EFC,0x07E0,0x1DF8,0x07E0,0x1DF0
- DC.W 0x07E0,0x1BC0,0x07E0,0x1B00,0x07F0,0x3480,0x03F8,0x6B00
- DC.W 0x03FF,0xD700,0x01FF,0xCE00,0x01FF,0xFE00,0x00FF,0xFC00
- DC.W 0x003F,0xF000,0x000F,0xC000,0x0000,0x0000,0x0000,0x0000
- ; this is the string in Finder's "At:" get info window
- DC.B 6,'S','C','S','I',' ','0',0
- @doIt LEA @icndata,addr
- }
- return(addr);
- }
-
-
- strcmp(s1, s2) /* compare two strings */
- char *s1, *s2;
- {
- for (; *s1 == *s2 ; s1++, s2++) if (!*s1) break;
- return (*s1 - *s2);
- }
-